#ifndef _BASIS_H_
#define _BASIS_H_

#include <fstream>
#include <math.h>

#include "Eigen/Dense"
#include "json.hpp"

#include "Mode.h"

#if defined(__GNUC__) || !defined(_MSC_VER)
#define _SAL_nop_impl_ X
#define _Group_(annos) _Group_impl_(annos _SAL_nop_impl_)
#define _Group_impl_(annos)
#define _SA_annotes3(n, pp1, pp2, pp3)

#define _SAL2_Source_(Name, args, annotes) _SA_annotes3(SAL_name, #Name, "", "2") _Group_(annotes _SAL_nop_impl_)
#define _Out_ _SAL2_Source_(_Out_, (), _Out_impl_)

#define _Pre1_impl_(p1)
#define _In_ _SAL2_Source_(_In_, (), _Pre1_impl_(__notnull_impl_notref) _Pre_valid_impl_ _Deref_pre1_impl_(__readaccess_impl_notref))
#endif

struct options
{
    int degree;
    CycleChoice cycleChoice;
    Dimension dim;
    int nu1;
    int nu2;
    BoundType boundType;
    Operators::Restriction rtype;
    Operators::Interpolation itype;
    int maxIteration;
    options();
    options(int degree, CycleChoice cycleChoice, Dimension dim, int nu1, int nu2, BoundType boundType, Operators::Restriction rtype, Operators::Interpolation itype, int maxIteration);
    bool load(const std::string &path);

    static options LoadFromFile(const std::string &path);
    nlohmann::json serialize();
};

inline double norm1(Eigen::VectorXd v)
{
    double res = 0;
    for (int i = 0; i < v.size(); i++)
        res += abs(v(i));
    return res;
}

inline double norm2(Eigen::VectorXd v)
{
    double res = 0;
    for (int i = 0; i < v.size(); i++)
        res += abs(v(i));
    return res;
}

inline bool operator<=(const Eigen::VectorXd &vd, double v)
{
    for (auto vi : vd)
    {
        if (abs(vi) > v)
            return false;
    }
    return true;
}

inline double vectorXdInftyNum(const Eigen::VectorXd &vd)
{
    double max = 0;
    for (auto vi : vd.cwiseAbs().array())
        if (max < vi)
            max = vi;
    return max;
}

#endif